July 2026 samba security fixes for v4.22
authorBjörn Jacke <bjacke@samba.org>
Wed, 15 Jul 2026 18:42:00 +0000 (18:42 +0000)
committerMichael Tokarev <mjt@tls.msk.ru>
Fri, 24 Jul 2026 13:14:13 +0000 (16:14 +0300)
Origin: upstream, https://bugzilla.samba.org/show_bug.cgi?id=16039
Forwarded: not-needed

From ea7530366da502e0a116467e4565304603eba5b1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Fri, 29 May 2026 12:43:13 +0200
Subject: [PATCH 01/23] CVE-2026-6949: ndr_dns: let ndr_pull_dns_res_rec()
 remember the start offset

In order to verify TSIG signatures we need a reliable way to
truncate the original dns_name_packet buffer before the
last additional dns_res_rec.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=16083

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <dbagnall@samba.org>
Gbp-Pq: Name 2026-jul-sec-update-bug-16039-v4-22-combined.patch

24 files changed:
ctdb/common/ctdb_io.c
ctdb/common/pkt_read.c
ctdb/common/sock_daemon.c
ctdb/doc/ctdb.7.xml
ctdb/protocol/protocol_basic.c
ctdb/protocol/protocol_types.c
ctdb/server/ctdb_call.c
ctdb/server/ctdb_client.c
ctdb/server/ctdb_control.c
ctdb/server/ctdb_ltdb_server.c
ctdb/server/ctdb_persistent.c
ctdb/server/ctdb_recover.c
ctdb/server/ctdb_traverse.c
ctdb/server/ctdb_update_record.c
librpc/idl/dns.idl
librpc/ndr/ndr_dns.c
source4/dns_server/dns_crypto.c
source4/dns_server/dns_query.c
source4/dsdb/samdb/ldb_modules/audit_util.c
source4/dsdb/samdb/ldb_modules/rootdse.c
source4/dsdb/samdb/ldb_modules/wscript_build_server
source4/kdc/kpasswd-service.c
source4/ldap_server/ldap_backend.c
source4/librpc/tests/dns-decode_dns_name_packet-hex.txt

index 9ac9b84a7fb2dd5930874f0ceb0892509db08fd7..346a7456f471554c16a7db60b3eb37aacd75bdc1 100644 (file)
@@ -115,7 +115,25 @@ static void queue_process(struct ctdb_queue *queue)
                return;
        }
 
-       /* Extract complete packet */
+       /*
+        * Extract complete packet
+        *
+        * Yes, this allows an out-of-memory denial of service (DoS)
+        * attack if a number of packets are received with
+        * unreasonable packet sizes, noting that the maximum packet
+        * size is ~4GB.  The CTDB protocol was not designed with a
+        * maximum packet size in mind, so CTDB may send very large
+        * valid packets.  This means that imposing an arbitrary limit
+        * on incoming packets is not reasonable.  Arguments that
+        * header fields, such as magic and/or version, should be
+        * validated before allocating a packet buffer are spurious
+        * from a security perspective because an attacker attempting
+        * DoS can send packets with valid headers.  The private
+        * network and ctdbd socket should be secured against
+        * untrusted access, so reports that this possible DoS vector
+        * represents a security issue will be ignored.  See the
+        * "Private addresses" section in ctdb(7) for more details.
+        */
        data = talloc_memdup(queue->data_pool,
                             queue->buffer.data + queue->buffer.offset,
                             pkt_size);
index 212ace54bbd824450a7fd9f05ee78dc150ae758d..8877f6aa368fea414fea063a9d3f647d6118d020 100644 (file)
@@ -146,6 +146,25 @@ void pkt_read_handler(struct tevent_context *ev, struct tevent_fd *fde,
                return;
        }
 
+       /*
+        * Allocate buffer for entire packet
+        *
+        * Yes, this allows an out-of-memory denial of service (DoS)
+        * attack if a number of packets are received with
+        * unreasonable packet sizes, noting that the maximum packet
+        * size is ~4GB.  The CTDB protocol was not designed with a
+        * maximum packet size in mind, so CTDB may send very large
+        * valid packets.  This means that imposing an arbitrary limit
+        * on incoming packets is not reasonable.  Arguments that
+        * header fields, such as magic and/or version, should be
+        * validated before allocating a packet buffer are spurious
+        * from a security perspective because an attacker attempting
+        * DoS can send packets with valid headers.  The private
+        * network and ctdbd socket should be secured against
+        * untrusted access, so reports that this possible DoS vector
+        * represents a security issue will be ignored.  See the
+        * "Private addresses" section in ctdb(7) for more details.
+        */
        if (state->use_fixed) {
                /* switch to dynamic buffer */
                tmp = talloc_array(state, uint8_t, state->total + more);
index e31a36445b51c856f87189cf20333e59cc8f8eba..ed0ccfd70efa81ea19c0f1390a983ee0f063ad1d 100644 (file)
@@ -204,6 +204,8 @@ static int sock_client_context_destructor(
 static int socket_setup(const char *sockpath, bool remove_before_use)
 {
        struct sockaddr_un addr;
+       const char *t;
+       bool test_mode_enabled = false;
        size_t len;
        int ret, fd;
 
@@ -240,6 +242,30 @@ static int socket_setup(const char *sockpath, bool remove_before_use)
                return -1;
        }
 
+       t = getenv("CTDB_TEST_MODE");
+       if (t != NULL) {
+               test_mode_enabled = true;
+       }
+
+       if (!test_mode_enabled) {
+               /* Behaviour of fchown(2) is undefined on sockets */
+               ret = chown(sockpath, geteuid(), getegid());
+               if (ret != 0) {
+                       D_ERR("Unable to secure (chown) socket '%s'\n",
+                             sockpath);
+                       close(fd);
+                       return -1;
+               }
+       }
+
+       /* Behaviour of fchmod(2) is undefined on sockets */
+       ret = chmod(sockpath, 0700);
+       if (ret != 0) {
+               D_ERR("Unable to secure (chmod) socket '%s'\n", sockpath);
+               close(fd);
+               return -1;
+       }
+
        ret = listen(fd, 10);
        if (ret != 0) {
                D_ERR("socket listen failed - %s\n", sockpath);
index be2dd5e5b65c3cdacfc7d3a8c7575f936bb9878d..9087acf987063bc11eaf0461ee566f2b6aa2193a 100644 (file)
        services provided by the cluster.
       </para>
       <para>
-        It is strongly recommended that the private addresses are
-        configured on a private network that is separate from client
-        networks.  This is because the CTDB protocol is both
-        unauthenticated and unencrypted.  If clients share the private
-        network then steps need to be taken to stop injection of
-        packets to relevant ports on the private addresses.  It is
-        also likely that CTDB protocol traffic between nodes could
-        leak sensitive information if it can be intercepted.
+       It is strongly recommended that the private addresses are
+       configured on a private network that is separate from client
+       networks.  This is because <emphasis>the CTDB protocol is both
+       unauthenticated and unencrypted</emphasis>.  If clients share
+       the private network then steps need to be taken to stop
+       injection of packets to relevant ports on the private
+       addresses.  It is also likely that CTDB protocol traffic
+       between nodes could leak sensitive information if it can be
+       intercepted or manipulated.
+      </para>
+      <para>
+       In summary, the private network should not be accessible by
+       untrusted sources.  We can't say "must not" because this can't
+       be enforced by CTDB, so we can only make a strong
+       recommendation. However, for this reason, future bugs in the
+       CTDB protocol used over the private network will be fixed as
+       regular bugs without following the Samba security process.
+       The same is true of local CTDB protocols used for Unix domain
+       socket and other communication.
       </para>
 
       <para>
index 42f207790d024370b7f2c379f02fa40b8a25da81..750ea8f9fe7434644e5c7b9b19643fd0f6253717 100644 (file)
@@ -247,7 +247,8 @@ void ctdb_string_push(const char **in, uint8_t *buf, size_t *npush)
 int ctdb_string_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                     const char **out, size_t *npull)
 {
-       const char *str;
+       const char *str = NULL;
+       size_t len = 0;
 
        if (buflen > UINT32_MAX) {
                return EMSGSIZE;
@@ -265,7 +266,14 @@ int ctdb_string_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
        }
 
        *out = str;
-       *npull = ctdb_string_len(&str);
+       /*
+        * Avoid claiming to have consumed more than buflen.
+        * ctdb_string_len() returns buflen + 1 if there is no
+        * NUL-terminator within buflen, so no NUL was actually
+        * consumed (so no +1 needed).
+        */
+       len = ctdb_string_len(&str);
+       *npull = MIN(buflen, len);
        return 0;
 }
 
@@ -311,7 +319,8 @@ int ctdb_stringn_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
        if (ret != 0) {
                return ret;
        }
-       offset += np;
+       /* Always consume the specified number bytes */
+       offset += u32;
 
        *npull = offset;
        return 0;
index 0eb1923207e9a78f0f1e5f0b39bab43598d948cf..8cbdaee5106470e2ae238dc79a26dfa42503b633 100644 (file)
@@ -838,6 +838,7 @@ int ctdb_statistics_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                              size_t *npull)
 {
        struct ctdb_statistics_list *val;
+       struct ctdb_statistics dummy = {};
        size_t offset = 0, np;
        int ret, i;
 
@@ -863,6 +864,11 @@ int ctdb_statistics_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_statistics_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->stats = talloc_array(val, struct ctdb_statistics, val->num);
        if (val->stats == NULL) {
                ret = ENOMEM;
@@ -925,6 +931,7 @@ int ctdb_vnn_map_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
        struct ctdb_vnn_map *val;
        size_t offset = 0, np;
        uint32_t i;
+       uint32_t dummy = 0;
        int ret;
 
        val = talloc(mem_ctx, struct ctdb_vnn_map);
@@ -950,6 +957,11 @@ int ctdb_vnn_map_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->size * ctdb_uint32_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->map = talloc_array(val, uint32_t, val->size);
        if (val->map == NULL) {
                ret = ENOMEM;
@@ -1082,6 +1094,7 @@ int ctdb_dbid_map_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                       struct ctdb_dbid_map **out, size_t *npull)
 {
        struct ctdb_dbid_map *val;
+       struct ctdb_dbid dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -1102,6 +1115,11 @@ int ctdb_dbid_map_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_dbid_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->dbs = talloc_array(val, struct ctdb_dbid, val->num);
        if (val->dbs == NULL) {
                ret = ENOMEM;
@@ -2362,6 +2380,7 @@ int ctdb_connection_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                              struct ctdb_connection_list **out, size_t *npull)
 {
        struct ctdb_connection_list *val;
+       struct ctdb_connection dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -2382,6 +2401,11 @@ int ctdb_connection_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_connection_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->conn = talloc_array(val, struct ctdb_connection, val->num);
        if (val->conn == NULL) {
                ret = ENOMEM;
@@ -3399,6 +3423,7 @@ int ctdb_tickle_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                           struct ctdb_tickle_list **out, size_t *npull)
 {
        struct ctdb_tickle_list *val;
+       struct ctdb_connection dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -3426,6 +3451,11 @@ int ctdb_tickle_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_connection_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->conn = talloc_array(val, struct ctdb_connection, val->num);
        if (val->conn == NULL) {
                ret = ENOMEM;
@@ -3744,6 +3774,7 @@ int ctdb_public_ip_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                             struct ctdb_public_ip_list **out, size_t *npull)
 {
        struct ctdb_public_ip_list *val;
+       struct ctdb_public_ip dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -3764,6 +3795,11 @@ int ctdb_public_ip_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_public_ip_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->ip = talloc_array(val, struct ctdb_public_ip, val->num);
        if (val->ip == NULL) {
                ret = ENOMEM;
@@ -3899,6 +3935,7 @@ int ctdb_node_map_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                       struct ctdb_node_map **out, size_t *npull)
 {
        struct ctdb_node_map *val;
+       struct ctdb_node_and_flags dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -3919,6 +3956,12 @@ int ctdb_node_map_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_node_and_flags_len(&dummy) >
+           buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->node = talloc_array(val, struct ctdb_node_and_flags, val->num);
        if (val->node == NULL) {
                ret = ENOMEM;
@@ -4099,6 +4142,7 @@ int ctdb_script_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                          struct ctdb_script_list **out, size_t *npull)
 {
        struct ctdb_script_list *val;
+       struct ctdb_script dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -4132,6 +4176,12 @@ int ctdb_script_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num_scripts * ctdb_script_len(&dummy)
+           > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->script = talloc_array(val, struct ctdb_script, val->num_scripts);
        if (val->script == NULL) {
                ret = ENOMEM;
@@ -4375,6 +4425,7 @@ int ctdb_iface_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                         struct ctdb_iface_list **out, size_t *npull)
 {
        struct ctdb_iface_list *val;
+       struct ctdb_iface dummy = {};
        size_t offset = 0, np;
        uint32_t i;
        int ret;
@@ -4395,6 +4446,11 @@ int ctdb_iface_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
                goto done;
        }
 
+       if ((uint64_t)val->num * ctdb_iface_len(&dummy) > buflen - offset) {
+               ret = EMSGSIZE;
+               goto fail;
+       }
+
        val->iface = talloc_array(val, struct ctdb_iface, val->num);
        if (val->iface == NULL) {
                ret = ENOMEM;
@@ -5322,6 +5378,10 @@ int ctdb_g_lock_list_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
 
        val->num = buflen / ctdb_g_lock_len(&lock);
 
+       /*
+        * No array count pre-check needed because val->num is
+        * calculated from buflen
+        */
        val->lock = talloc_array(val, struct ctdb_g_lock, val->num);
        if (val->lock == NULL) {
                ret = ENOMEM;
index a51a92d3e45603005650788f9fd88673657e178c..cfeca78061c6d777a8ea6a77d82cd525e7798297 100644 (file)
@@ -583,6 +583,9 @@ static int dmaster_defer_add(struct ctdb_db_context *ctdb_db,
 void ctdb_request_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
        struct ctdb_req_dmaster_old *c = (struct ctdb_req_dmaster_old *)hdr;
+       size_t req_dmaster_header_len = offsetof(struct ctdb_req_dmaster_old,
+                                                data);
+       size_t buf_len = hdr->length;
        TDB_DATA key, data, data2;
        struct ctdb_ltdb_header header;
        struct ctdb_db_context *ctdb_db;
@@ -590,6 +593,13 @@ void ctdb_request_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr
        size_t len;
        int ret;
 
+       if (buf_len < req_dmaster_header_len ||
+           c->keylen > buf_len - req_dmaster_header_len ||
+           c->datalen > buf_len - req_dmaster_header_len - c->keylen) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        key.dptr = c->data;
        key.dsize = c->keylen;
        data.dptr = c->data + c->keylen;
@@ -917,6 +927,8 @@ sort_keys:
 void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
        struct ctdb_req_call_old *c = (struct ctdb_req_call_old *)hdr;
+       size_t req_call_header_len = offsetof(struct ctdb_req_call_old, data);
+       size_t buf_len = hdr->length;
        TDB_DATA data;
        struct ctdb_reply_call_old *r;
        int ret, len;
@@ -925,6 +937,13 @@ void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
        struct ctdb_db_context *ctdb_db;
        int tmp_count, bucket;
 
+       if (buf_len < req_call_header_len ||
+           c->keylen > buf_len - req_call_header_len  ||
+           c->calldatalen > buf_len - req_call_header_len - c->keylen) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        if (ctdb->methods == NULL) {
                DEBUG(DEBUG_INFO,(__location__ " Failed ctdb_request_call. Transport is DOWN\n"));
                return;
@@ -1199,8 +1218,17 @@ void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 void ctdb_reply_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
        struct ctdb_reply_call_old *c = (struct ctdb_reply_call_old *)hdr;
+       size_t reply_call_header_len = offsetof(struct ctdb_reply_call_old,
+                                               data);
+       size_t buf_len = hdr->length;
        struct ctdb_call_state *state;
 
+       if (buf_len < reply_call_header_len ||
+           c->datalen > buf_len - reply_call_header_len) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        state = reqid_find(ctdb->idr, hdr->reqid, struct ctdb_call_state);
        if (state == NULL) {
                DEBUG(DEBUG_ERR, (__location__ " reqid %u not found\n", hdr->reqid));
@@ -1296,12 +1324,22 @@ finished_ro:
 void ctdb_reply_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
        struct ctdb_reply_dmaster_old *c = (struct ctdb_reply_dmaster_old *)hdr;
+       size_t reply_dmaster_header_len = offsetof(struct ctdb_reply_dmaster_old,
+                                                  data);
+       size_t buf_len = hdr->length;
        struct ctdb_db_context *ctdb_db;
        TDB_DATA key, data;
        uint32_t record_flags = 0;
        size_t len;
        int ret;
 
+       if (buf_len < reply_dmaster_header_len ||
+           c->keylen > buf_len - reply_dmaster_header_len ||
+           c->datalen > buf_len - reply_dmaster_header_len - c->keylen) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        ctdb_db = find_ctdb_db(ctdb, c->db_id);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unknown db_id 0x%x in ctdb_reply_dmaster\n", c->db_id));
index c9edb1d554cc04ec442038d76ba584f0d8c627c2..84e8e3d2da6ce368a53e667c5900624657c95da6 100644 (file)
@@ -193,8 +193,17 @@ void ctdb_request_message(struct ctdb_context *ctdb,
                          struct ctdb_req_header *hdr)
 {
        struct ctdb_req_message_old *c = (struct ctdb_req_message_old *)hdr;
+       size_t req_message_header_len = offsetof(struct ctdb_req_message_old,
+                                                data);
+       size_t buf_len = hdr->length;
        TDB_DATA data;
 
+       if (buf_len < req_message_header_len ||
+           c->datalen > buf_len - req_message_header_len) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        data.dsize = c->datalen;
        data.dptr = talloc_memdup(c, &c->data[0], c->datalen);
        if (data.dptr == NULL) {
index a51795f340a5dd9b24f1fa91afb7599b4c284250..cfd80330c410c36ad19889a1e86599d3ac5df050 100644 (file)
@@ -933,11 +933,20 @@ void ctdb_request_control_reply(struct ctdb_context *ctdb, struct ctdb_req_contr
 void ctdb_request_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
        struct ctdb_req_control_old *c = (struct ctdb_req_control_old *)hdr;
+       size_t req_control_header_len = offsetof(struct ctdb_req_control_old,
+                                                data);
+       size_t buf_len = hdr->length;
        TDB_DATA data, *outdata;
        int32_t status;
        bool async_reply = false;
        const char *errormsg = NULL;
 
+       if (buf_len < req_control_header_len ||
+           c->datalen > buf_len - req_control_header_len) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        data.dptr = &c->data[0];
        data.dsize = c->datalen;
 
@@ -957,10 +966,20 @@ void ctdb_request_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr
 void ctdb_reply_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr)
 {
        struct ctdb_reply_control_old *c = (struct ctdb_reply_control_old *)hdr;
+       size_t reply_control_header_len = offsetof(struct ctdb_reply_control_old,
+                                                  data);
+       size_t buf_len = hdr->length;
        TDB_DATA data;
        struct ctdb_control_state *state;
        const char *errormsg = NULL;
 
+       if (buf_len < reply_control_header_len ||
+           c->datalen > buf_len - reply_control_header_len ||
+           c->errorlen > buf_len - reply_control_header_len - c->datalen) {
+               DBG_WARNING("Invalid packet\n");
+               return;
+       }
+
        state = reqid_find(ctdb->idr, hdr->reqid, struct ctdb_control_state);
        if (state == NULL) {
                DEBUG(DEBUG_ERR,("pnn %u Invalid reqid %u in ctdb_reply_control\n",
index 8d03062b97a89b050040d02038e496abf10b2737..2433b39b836a29f0fdd17c9254fe4dea0e7ca16f 100644 (file)
@@ -1109,8 +1109,15 @@ int32_t ctdb_control_db_attach(struct ctdb_context *ctdb,
        struct ctdb_db_context *db;
        struct ctdb_node *node = ctdb->nodes[ctdb->pnn];
        struct ctdb_client *client = NULL;
+       char *t = NULL;
        uint32_t opcode;
 
+       t = memchr(indata.dptr, '\0', indata.dsize);
+       if (t == NULL) {
+               DBG_ERR("Invalid packet\n");
+               return -1;
+       }
+
        if (ctdb->tunable.allow_client_db_attach == 0) {
                DEBUG(DEBUG_ERR, ("DB Attach to database %s denied by tunable "
                                  "AllowClientDBAccess == 0\n", db_name));
index 26717441d176bae4f98366be575c253670197515..1b0d141155325bcd86ecd25bf32bbbaa19e41906 100644 (file)
@@ -182,6 +182,16 @@ int32_t ctdb_control_trans3_commit(struct ctdb_context *ctdb,
        struct ctdb_marshall_buffer *m = (struct ctdb_marshall_buffer *)recdata.dptr;
        struct ctdb_db_context *ctdb_db;
 
+       if (recdata.dsize < offsetof(struct ctdb_marshall_buffer, data)) {
+               DBG_ERR("Invalid packet\n");
+               return -1;
+       }
+       if (m->count >
+           recdata.dsize - offsetof(struct ctdb_marshall_buffer, data)) {
+               DBG_ERR("Invalid packet\n");
+               return -1;
+       }
+
        if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) {
                DEBUG(DEBUG_INFO,("rejecting ctdb_control_trans3_commit when recovery active\n"));
                return -1;
index 18dc250f5ce067ab7036407ca9588452762828f9..b74dca58c8d58a0579b0e27c5e2cd276f6011d33 100644 (file)
@@ -36,6 +36,8 @@
 #include "ctdb_private.h"
 #include "ctdb_client.h"
 
+#include "protocol/protocol_private.h"
+
 #include "common/system.h"
 #include "common/common.h"
 #include "common/logging.h"
@@ -67,24 +69,32 @@ ctdb_control_getvnnmap(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA inda
 int
 ctdb_control_setvnnmap(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA indata, TDB_DATA *outdata)
 {
-       struct ctdb_vnn_map_wire *map = (struct ctdb_vnn_map_wire *)indata.dptr;
+       struct ctdb_vnn_map *new = NULL;
+       size_t npull = 0;
+       int ret = 0;
 
        if (ctdb->recovery_mode != CTDB_RECOVERY_ACTIVE) {
                DEBUG(DEBUG_ERR, ("Attempt to set vnnmap when not in recovery\n"));
                return -1;
        }
 
-       talloc_free(ctdb->vnn_map);
-
-       ctdb->vnn_map = talloc(ctdb, struct ctdb_vnn_map);
-       CTDB_NO_MEMORY(ctdb, ctdb->vnn_map);
-
-       ctdb->vnn_map->generation = map->generation;
-       ctdb->vnn_map->size       = map->size;
-       ctdb->vnn_map->map = talloc_array(ctdb->vnn_map, uint32_t, map->size);
-       CTDB_NO_MEMORY(ctdb, ctdb->vnn_map->map);
+       ret = ctdb_vnn_map_pull(indata.dptr, indata.dsize, ctdb, &new, &npull);
+       if (ret != 0) {
+               switch (ret) {
+               case ENOMEM:
+                       DBG_ERR("Memory allocation error\n");
+                       break;
+               case EMSGSIZE:
+                       DBG_ERR("Invalid packet\n");
+                       break;
+               default:
+                       DBG_ERR("Unexpected error (%d)\n", ret);
+               }
+               return -1;
+       }
 
-       memcpy(ctdb->vnn_map->map, map->map, sizeof(uint32_t)*map->size);
+       talloc_free(ctdb->vnn_map);
+       ctdb->vnn_map = new;
 
        return 0;
 }
@@ -1052,6 +1062,12 @@ int32_t ctdb_control_try_delete_records(struct ctdb_context *ctdb, TDB_DATA inda
                return -1;
        }
 
+       if (reply->count >
+           indata.dsize - offsetof(struct ctdb_marshall_buffer, data)) {
+               DBG_ERR("Invalid packet\n");
+               return -1;
+       }
+
        ctdb_db = find_ctdb_db(ctdb, reply->db_id);
        if (!ctdb_db) {
                DEBUG(DEBUG_ERR,(__location__ " Unknown db 0x%08x\n", reply->db_id));
index 4865dcc94f016f5d8f8ffaf6b7359e7a5125127d..094f824bf9d88b8b0daa24812846b4e3decf54ca 100644 (file)
@@ -561,13 +561,17 @@ int32_t ctdb_control_traverse_all(struct ctdb_context *ctdb, TDB_DATA data, TDB_
 int32_t ctdb_control_traverse_data(struct ctdb_context *ctdb, TDB_DATA data, TDB_DATA *outdata)
 {
        struct ctdb_rec_data_old *d = (struct ctdb_rec_data_old *)data.dptr;
+       size_t rec_data_header_len = offsetof(struct ctdb_rec_data_old, data);
        struct ctdb_traverse_all_handle *state;
        TDB_DATA key;
        ctdb_traverse_fn_t callback;
        void *private_data;
 
-       if (data.dsize < sizeof(uint32_t) || data.dsize != d->length) {
-               DEBUG(DEBUG_ERR,("Bad record size in ctdb_control_traverse_data\n"));
+       if (data.dsize < rec_data_header_len ||
+           data.dsize != d->length ||
+           d->keylen > d->length - rec_data_header_len ||
+           d->datalen > d->length - rec_data_header_len - d->keylen) {
+               DBG_ERR("Invalid packet\n");
                return -1;
        }
 
index 405499c81e261a7f9beea1bce8fc116617b273e3..154b142741095f9ae402ee32d54ade3fdd41efe5 100644 (file)
@@ -318,6 +318,16 @@ int32_t ctdb_control_update_record(struct ctdb_context *ctdb,
        struct childwrite_handle *handle;
        struct ctdb_marshall_buffer *m = (struct ctdb_marshall_buffer *)recdata.dptr;
 
+       if (recdata.dsize < offsetof(struct ctdb_marshall_buffer, data)) {
+               DBG_ERR("Invalid packet\n");
+               return -1;
+       }
+       if (m->count >
+           recdata.dsize - offsetof(struct ctdb_marshall_buffer, data)) {
+               DBG_ERR("Invalid packet\n");
+               return -1;
+       }
+
        if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) {
                DEBUG(DEBUG_INFO,("rejecting ctdb_control_update_record when recovery active\n"));
                return -1;
index ec8668aa212672344f39cba61606791de28cd0ac..7cb316b2b126d59256d6cfddd5e4ed99c4cf24e9 100644 (file)
@@ -251,6 +251,12 @@ interface dns
        } dns_rdata;
 
        typedef [flag(LIBNDR_PRINT_ARRAY_HEX|NDR_NOALIGN),nopush,nopull] struct {
+               /*
+                * This is the start offset of this
+                * dns_res_rec relative to the start of
+                * dns_name_packet buffer.
+                */
+               [ignore] uint32 start_ndr_offset;
                dns_string name;
                dns_qtype  rr_type;
                dns_qclass rr_class;
index 9cc54c1a9727e40feabcf0dc2171bdc6c58cc891..f1de40fd7e851f827d7b8fdff30d1f62d111d3a3 100644 (file)
@@ -276,6 +276,17 @@ _PUBLIC_ enum ndr_err_code ndr_pull_dns_res_rec(struct ndr_pull *ndr,
        ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX |
                                   LIBNDR_FLAG_NOALIGN);
        if (ndr_flags & NDR_SCALARS) {
+               /*
+                * Remember the start offset in order
+                * to know how to truncate before the
+                * last dns_res_rec in order to do
+                * the correct TSIG calculation.
+                *
+                * Note that we just set LIBNDR_FLAG_NOALIGN
+                * above, so ndr_pull_align is a no-op.
+                */
+               r->start_ndr_offset = ndr->offset;
+
                NDR_CHECK(ndr_pull_align(ndr, 4));
                NDR_CHECK(ndr_pull_dns_string(ndr, NDR_SCALARS, &r->name));
                NDR_CHECK(ndr_pull_dns_qtype(ndr, NDR_SCALARS, &r->rr_type));
index d30e971508631ec6fec7aec55a60a9143e03cf49..722213bfd0aa62388a4fa8601256142c6015fb9b 100644 (file)
@@ -36,6 +36,8 @@ static WERROR dns_copy_tsig(TALLOC_CTX *mem_ctx,
                            struct dns_res_rec *old,
                            struct dns_res_rec *new_rec)
 {
+       new_rec->start_ndr_offset = 0;
+
        new_rec->name = talloc_strdup(mem_ctx, old->name);
        W_ERROR_HAVE_NO_MEMORY(new_rec->name);
 
@@ -101,7 +103,7 @@ WERROR dns_verify_tsig(struct dns_server *dns,
        NTSTATUS status;
        enum ndr_err_code ndr_err;
        uint16_t i, arcount = 0;
-       DATA_BLOB tsig_blob, fake_tsig_blob, sig;
+       DATA_BLOB fake_tsig_blob, sig;
        uint8_t *buffer = NULL;
        size_t buffer_len = 0, packet_len = 0;
        struct dns_server_tkey *tkey = NULL;
@@ -131,6 +133,22 @@ WERROR dns_verify_tsig(struct dns_server *dns,
        state->sign = true;
        DBG_DEBUG("Got TSIG\n");
 
+       /*
+        * We need to keep the input packet exactly like we got it,
+        * but we need to cut off the tsig record.
+        *
+        * DNS packets can not be larger than UINT16_MAX,
+        * the size of the UDP payload is uint16_t and
+        * there's a uint16_t length header for TCP.
+        *
+        * And the start offset of the last
+        * additional record can't be larger than
+        * the whole packet.
+        */
+       packet_len = packet->additional[i].start_ndr_offset;
+       SMB_ASSERT(in->length <= UINT16_MAX);
+       SMB_ASSERT(in->length > packet_len);
+
        state->tsig = talloc_zero(state->mem_ctx, struct dns_res_rec);
        if (state->tsig == NULL) {
                return WERR_NOT_ENOUGH_MEMORY;
@@ -205,14 +223,6 @@ WERROR dns_verify_tsig(struct dns_server *dns,
        check_rec->other_size = 0;
        check_rec->other_data = NULL;
 
-       ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, state->tsig,
-               (ndr_push_flags_fn_t)ndr_push_dns_res_rec);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               DEBUG(1, ("Failed to push packet: %s!\n",
-                         ndr_errstr(ndr_err)));
-               return DNS_ERR(SERVER_FAILURE);
-       }
-
        ndr_err = ndr_push_struct_blob(&fake_tsig_blob, mem_ctx, check_rec,
                (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -221,9 +231,19 @@ WERROR dns_verify_tsig(struct dns_server *dns,
                return DNS_ERR(SERVER_FAILURE);
        }
 
-       /* we need to work some magic here. we need to keep the input packet
-        * exactly like we got it, but we need to cut off the tsig record */
-       packet_len = in->length - tsig_blob.length;
+       /*
+        * We already asserted packet_len < UINT16_MAX
+        * above, and struct ndr_push has alloc_size and
+        * offset as uint32_t, so it's really unlikely
+        * to overflow buffer_len. For SIZE_MAX == UINT32_MAX,
+        * the following check might be important, but
+        * just lets do it always.
+        */
+       if (fake_tsig_blob.length > (SIZE_MAX - UINT16_MAX)) {
+               DBG_WARNING("fake_tsig_blob.length=%zu too large!\n",
+                           fake_tsig_blob.length);
+               return DNS_ERR(SERVER_FAILURE);
+       }
        buffer_len = packet_len + fake_tsig_blob.length;
        buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
        if (buffer == NULL) {
index 1f46ee0aa197e2f510c066e38e421998c35c8f26..2998dbee060351d319f10a619f99a8327b9ddaef 100644 (file)
@@ -654,7 +654,8 @@ static WERROR handle_authoritative_recv(struct tevent_req *req)
        return WERR_OK;
 }
 
-static NTSTATUS create_tkey(struct dns_server *dns,
+static NTSTATUS create_tkey(TALLOC_CTX *mem_ctx,
+                           struct dns_server *dns,
                            const char* name,
                            const char* algorithm,
                            const struct tsocket_address *remote_address,
@@ -662,7 +663,6 @@ static NTSTATUS create_tkey(struct dns_server *dns,
                            struct dns_server_tkey **tkey)
 {
        NTSTATUS status;
-       struct dns_server_tkey_store *store = dns->tkeys;
        struct dns_server_tkey *k = NULL;
 
        if (strcmp(algorithm, "gss-tsig") == 0) {
@@ -673,20 +673,20 @@ static NTSTATUS create_tkey(struct dns_server *dns,
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       k = talloc_zero(store, struct dns_server_tkey);
+       k = talloc_zero(mem_ctx, struct dns_server_tkey);
        if (k == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               goto nomem;
        }
 
        k->name = talloc_strdup(k, name);
 
        if (k->name  == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               goto nomem;
        }
 
        k->algorithm = talloc_strdup(k, algorithm);
        if (k->algorithm == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               goto nomem;
        }
 
        /*
@@ -704,8 +704,7 @@ static NTSTATUS create_tkey(struct dns_server *dns,
                                                &k->gensec);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
-               *tkey = NULL;
-               return status;
+               goto fail;
        }
 
        gensec_want_feature(k->gensec, GENSEC_FEATURE_SIGN);
@@ -715,8 +714,7 @@ static NTSTATUS create_tkey(struct dns_server *dns,
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("Failed to set remote address into GENSEC: %s\n",
                          nt_errstr(status)));
-               *tkey = NULL;
-               return status;
+               goto fail;
        }
 
        status = gensec_set_local_address(k->gensec,
@@ -724,8 +722,7 @@ static NTSTATUS create_tkey(struct dns_server *dns,
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("Failed to set local address into GENSEC: %s\n",
                          nt_errstr(status)));
-               *tkey = NULL;
-               return status;
+               goto fail;
        }
 
        status = gensec_start_mech_by_oid(k->gensec, GENSEC_OID_SPNEGO);
@@ -733,18 +730,29 @@ static NTSTATUS create_tkey(struct dns_server *dns,
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("Failed to start GENSEC server code: %s\n",
                          nt_errstr(status)));
-               *tkey = NULL;
-               return status;
+               goto fail;
        }
 
+       *tkey = k;
+       return NT_STATUS_OK;
+
+nomem:
+       status = NT_STATUS_NO_MEMORY;
+fail:
+       TALLOC_FREE(k);
+       *tkey = NULL;
+       return status;
+}
+
+static void add_tkey(struct dns_server_tkey_store *store,
+                    struct dns_server_tkey **tkey)
+{
        TALLOC_FREE(store->tkeys[store->next_idx]);
 
-       store->tkeys[store->next_idx] = k;
+       store->tkeys[store->next_idx] = talloc_move(store->tkeys, tkey);
+
        (store->next_idx)++;
        store->next_idx %= store->size;
-
-       *tkey = k;
-       return NT_STATUS_OK;
 }
 
 static NTSTATUS accept_gss_ticket(TALLOC_CTX *mem_ctx,
@@ -860,6 +868,7 @@ static WERROR handle_tkey(struct dns_server *dns,
                struct dns_server_tkey *tkey;
                DATA_BLOB key;
                DATA_BLOB reply;
+               bool created = false;
 
                tkey = dns_find_tkey(dns->tkeys, in->questions[0].name);
                if (tkey != NULL && tkey->complete) {
@@ -870,15 +879,19 @@ static WERROR handle_tkey(struct dns_server *dns,
                }
 
                if (tkey == NULL) {
-                       status  = create_tkey(dns, in->questions[0].name,
-                                             in_tkey->rdata.tkey_record.algorithm,
-                                             state->remote_address,
-                                             state->local_address,
-                                             &tkey);
+                       status = create_tkey(
+                               mem_ctx,
+                               dns,
+                               in->questions[0].name,
+                               in_tkey->rdata.tkey_record.algorithm,
+                               state->remote_address,
+                               state->local_address,
+                               &tkey);
                        if (!NT_STATUS_IS_OK(status)) {
                                ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY;
                                return ntstatus_to_werror(status);
                        }
+                       created = true;
                }
 
                key.data = in_tkey->rdata.tkey_record.key_data;
@@ -903,6 +916,9 @@ static WERROR handle_tkey(struct dns_server *dns,
                        if (state->key_name == NULL) {
                                return WERR_NOT_ENOUGH_MEMORY;
                        }
+                       if (created) {
+                               add_tkey(dns->tkeys, &tkey);
+                       }
                } else {
                        DEBUG(1, ("GSS key negotiation returned %s\n", nt_errstr(status)));
                        ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY;
index 8fe7d8332d2fc6dda4f5ff1d1488d5339089eaf6..25f90ee3db395281cf51161674bb8336778352b3 100644 (file)
@@ -694,3 +694,183 @@ failure:
        DBG_ERR("Unable to create ldb attributes JSON audit message\n");
        return attributes;
 }
+
+/*
+ * @brief Generate a human readable string, detailing attributes in a message
+ *
+ * For modify operations each attribute is prefixed with the action.
+ * Normal values are enclosed in []
+ * Base64 values are enclosed in {}
+ * Truncated values are indicated by three trailing dots "..."
+ *
+ * @param[in]  ldb       the ldb_context
+ * @param[out] buffer    the attributes will be appended to the buffer.
+ *                       assumed to have been allocated via talloc.
+ * @param[in]  operation the operation type
+ * @param[in]  message   the message to process
+ *
+ * @return a pointer to buffer
+ *
+ */
+char *dsdb_audit_log_attributes(
+       struct ldb_context *ldb,
+       char *buffer,
+       enum ldb_request_type operation,
+       const struct ldb_message *message)
+{
+       size_t i, j;
+       for (i=0;i<message->num_elements;i++) {
+               if (i > 0) {
+                       buffer = talloc_asprintf_append_buffer(buffer, " ");
+               }
+
+               if (message->elements[i].name == NULL) {
+                       ldb_debug(
+                               ldb,
+                               LDB_DEBUG_ERROR,
+                               "Error: Invalid element name (NULL) at "
+                               "position %zu", i);
+                       return NULL;
+               }
+
+               if (operation == LDB_MODIFY) {
+                       const char *action =NULL;
+                       action = dsdb_audit_get_modification_action(
+                               message->elements[i].flags);
+                       buffer = talloc_asprintf_append_buffer(
+                               buffer,
+                               "%s: %s ",
+                               action,
+                               message->elements[i].name);
+               } else {
+                       buffer = talloc_asprintf_append_buffer(
+                               buffer,
+                               "%s ",
+                               message->elements[i].name);
+               }
+
+               if (dsdb_audit_redact_attribute(message->elements[i].name)) {
+                       /*
+                        * Do not log the value of any secret or password
+                        * attributes
+                        */
+                       buffer = talloc_asprintf_append_buffer(
+                               buffer,
+                               "[REDACTED SECRET ATTRIBUTE]");
+                       continue;
+               }
+
+               for (j=0;j<message->elements[i].num_values;j++) {
+                       struct ldb_val v;
+                       bool use_b64_encode = false;
+                       size_t length;
+                       if (j > 0) {
+                               buffer = talloc_asprintf_append_buffer(
+                                       buffer,
+                                       " ");
+                       }
+
+                       v = message->elements[i].values[j];
+                       length = MIN(MAX_LENGTH, v.length);
+                       use_b64_encode = ldb_should_b64_encode(ldb, &v);
+                       if (use_b64_encode) {
+                               const char *encoded = ldb_base64_encode(
+                                       buffer,
+                                       (char *)v.data,
+                                       length);
+                               buffer = talloc_asprintf_append_buffer(
+                                       buffer,
+                                       "{%s%s}",
+                                       encoded,
+                                       (v.length > MAX_LENGTH ? "..." : ""));
+                       } else {
+                               buffer = talloc_asprintf_append_buffer(
+                                       buffer,
+                                       "[%*.*s%s]",
+                                       (int)length,
+                                       (int)length,
+                                       (char *)v.data,
+                                       (v.length > MAX_LENGTH ? "..." : ""));
+                       }
+               }
+       }
+       return buffer;
+}
+
+/*
+ * @brief generate a human readable log entry detailing an ldb operation.
+ *
+ * Generate a human readable log entry detailing an ldb operation.
+ *
+ * @param[in] mem_ctx the talloc context owning the returned string.
+ * @param[in] module  the ldb module
+ * @param[in] request the request
+ * @param[in] reply   the result of the operation
+ *
+ * @return the log entry.
+ *
+ */
+char *dsdb_audit_operation_human_readable(
+       TALLOC_CTX *mem_ctx,
+       struct ldb_module *module,
+       const struct ldb_request *request,
+       const struct ldb_reply *reply)
+{
+       struct ldb_context *ldb = NULL;
+       const char *remote_host = NULL;
+       const struct tsocket_address *remote = NULL;
+       const struct dom_sid *sid = NULL;
+       struct dom_sid_buf user_sid;
+       const char *timestamp = NULL;
+       const char *op_name = NULL;
+       char *log_entry = NULL;
+       const char *dn = NULL;
+       const char *new_dn = NULL;
+       const struct ldb_message *message = NULL;
+
+       TALLOC_CTX *ctx = talloc_new(NULL);
+
+       ldb = ldb_module_get_ctx(module);
+
+       remote_host = dsdb_audit_get_remote_host(ldb, ctx);
+       remote = dsdb_audit_get_remote_address(ldb);
+       if (remote != NULL && dsdb_audit_is_system_session(module)) {
+               sid = dsdb_audit_get_actual_sid(ldb);
+       } else {
+               sid = dsdb_audit_get_user_sid(module);
+       }
+       timestamp = audit_get_timestamp(ctx);
+       op_name = dsdb_audit_get_operation_name(request);
+       dn = dsdb_audit_get_primary_dn(request);
+       new_dn = dsdb_audit_get_secondary_dn(request);
+
+       message = dsdb_audit_get_message(request);
+
+       log_entry = talloc_asprintf(
+               mem_ctx,
+               "[%s] at [%s] status [%s] "
+               "remote host [%s] SID [%s] DN [%s]",
+               op_name,
+               timestamp,
+               ldb_strerror(reply->error),
+               remote_host,
+               dom_sid_str_buf(sid, &user_sid),
+               dn);
+       if (new_dn != NULL) {
+               log_entry = talloc_asprintf_append_buffer(
+                       log_entry,
+                       " New DN [%s]",
+                       new_dn);
+       }
+       if (message != NULL) {
+               log_entry = talloc_asprintf_append_buffer(log_entry,
+                                                         " attributes [");
+               log_entry = dsdb_audit_log_attributes(ldb,
+                                                     log_entry,
+                                                     request->operation,
+                                                     message);
+               log_entry = talloc_asprintf_append_buffer(log_entry, "]");
+       }
+       TALLOC_FREE(ctx);
+       return log_entry;
+}
index 7541a2cf3762babe0e6c53f637ec81b4bc1fbfcb..9cc8477b8f8043ec3bf0c9c71d60f1cff3222d43 100644 (file)
@@ -28,6 +28,7 @@
 #include "dsdb/samdb/samdb.h"
 #include "version.h"
 #include "dsdb/samdb/ldb_modules/util.h"
+#include "dsdb/samdb/ldb_modules/audit_util_proto.h"
 #include "libcli/security/security.h"
 #include "librpc/ndr/libndr.h"
 #include "auth/auth.h"
@@ -746,18 +747,68 @@ static int rootdse_filter_controls(struct ldb_module *module, struct ldb_request
        return LDB_SUCCESS;
 }
 
-/* Ensure that anonymous users are not allowed to make anything other than rootDSE search operations */
-
+/*
+ * Ensure that anonymous users are not allowed to make anything other than
+ * rootDSE search operations and special dns like @MODULES are not allowed
+ * over an untrusted connection.
+ */
 static int rootdse_filter_operations(struct ldb_module *module, struct ldb_request *req)
 {
        struct auth_session_info *session_info;
        struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(module), struct rootdse_private_data);
        bool is_untrusted = ldb_req_is_untrusted(req);
        bool is_anonymous = true;
+       struct ldb_dn *dn = NULL;
+       struct ldb_dn *dn2 = NULL;
+
        if (is_untrusted == false) {
                return LDB_SUCCESS;
        }
 
+       switch (req->operation) {
+       case LDB_SEARCH:
+               dn = req->op.search.base;
+               break;
+       case LDB_ADD:
+               dn = req->op.add.message->dn;
+               break;
+       case LDB_MODIFY:
+               dn = req->op.mod.message->dn;
+               break;
+       case LDB_DELETE:
+               dn = req->op.del.dn;
+               break;
+       case LDB_RENAME:
+               dn = req->op.rename.olddn;
+               dn2 = req->op.rename.newdn;
+               break;
+       case LDB_EXTENDED:
+               break;
+       case LDB_REQ_REGISTER_CONTROL:
+       case LDB_REQ_REGISTER_PARTITION:
+               ldb_set_errstring(ldb_module_get_ctx(module), "Invalid OP");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (ldb_dn_is_special(dn)) {
+               struct ldb_reply reply = { .error = LDB_ERR_OPERATIONS_ERROR, };
+
+               D_ERR("CVE-2026-58221-ATTACK: %s\n",
+                       dsdb_audit_operation_human_readable(req, module, req, &reply));
+
+               ldb_set_errstring(ldb_module_get_ctx(module), "Invalid DN");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       if (ldb_dn_is_special(dn2)) {
+               struct ldb_reply reply = { .error = LDB_ERR_OPERATIONS_ERROR, };
+
+               D_ERR("CVE-2026-58221-ATTACK: %s\n",
+                       dsdb_audit_operation_human_readable(req, module, req, &reply));
+
+               ldb_set_errstring(ldb_module_get_ctx(module), "Invalid DN");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
        session_info = (struct auth_session_info *)ldb_get_opaque(
                ldb_module_get_ctx(module),
                DSDB_SESSION_INFO);
index 800ed2954c418de9312247d401e983ce2c9433f2..7582b64a93837f36e95f45364e60b0a9b4f22aeb 100644 (file)
@@ -185,7 +185,7 @@ bld.SAMBA_MODULE('ldb_rootdse',
        init_function='ldb_rootdse_module_init',
        module_init_name='ldb_init_module',
        internal_module=False,
-       deps='talloc samdb MESSAGING samba-security DSDB_MODULE_HELPERS RPC_NDR_IRPC'
+       deps='talloc samdb MESSAGING samba-security DSDB_MODULE_HELPERS DSDB_MODULE_HELPERS_AUDIT RPC_NDR_IRPC'
        )
 
 
index c671eb46d07027467a896667ed1e40d001a2f050..0284fb4f1334067ac3927a298bc83797e37ada9f 100644 (file)
@@ -137,7 +137,7 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
 
        ap_req_blob = data_blob_const(&request->data[HEADER_LEN], ap_req_len);
 
-       enc_data_len = len - ap_req_len;
+       enc_data_len = len - (ap_req_len + HEADER_LEN);
        enc_data_blob = data_blob_const(&request->data[HEADER_LEN + ap_req_len],
                                        enc_data_len);
 
index 986bc1db94172be73044a5bd133bf985a38c440f..4603935f0b0776a4a8118901d11159f7d7af55b9 100644 (file)
@@ -29,6 +29,7 @@
 #include "samba/service_stream.h"
 #include "dsdb/gmsa/util.h"
 #include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
 #include <ldb_errors.h>
 #include <ldb_module.h>
 #include "ldb_wrap.h"
@@ -1449,6 +1450,7 @@ static NTSTATUS ldapsrv_CompareRequest(struct ldapsrv_call *call)
        struct ldb_dn *dn;
        const char *attrs[1];
        const char *errstr = NULL;
+       const char *value = NULL;
        const char *filter = NULL;
        int result = LDAP_SUCCESS;
        int ldb_ret;
@@ -1461,21 +1463,31 @@ static NTSTATUS ldapsrv_CompareRequest(struct ldapsrv_call *call)
        dn = ldb_dn_new(local_ctx, samdb, req->dn);
        NT_STATUS_HAVE_NO_MEMORY(dn);
 
+       compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
+       NT_STATUS_HAVE_NO_MEMORY(compare_r);
+
        DBG_DEBUG("dn: [%s]\n", req->dn);
-       filter = talloc_asprintf(local_ctx, "(%s=%*s)", req->attribute,
-                                (int)req->value.length, req->value.data);
+
+       if (!ldb_valid_attr_name(req->attribute)) {
+               result = LDAP_INVALID_ATTRIBUTE_SYNTAX;
+               errstr = "Invalid Compare attribute name";
+               goto reply;
+       }
+       value = ldb_binary_encode(local_ctx, req->value);
+       NT_STATUS_HAVE_NO_MEMORY(value);
+
+       filter = talloc_asprintf(local_ctx, "%s=%s", req->attribute, value);
        NT_STATUS_HAVE_NO_MEMORY(filter);
 
        DBG_DEBUG("attribute: [%s]\n", filter);
 
        attrs[0] = NULL;
 
-       compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
-       NT_STATUS_HAVE_NO_MEMORY(compare_r);
-
        if (result == LDAP_SUCCESS) {
-               ldb_ret = ldb_search(samdb, local_ctx, &res,
-                                    dn, LDB_SCOPE_BASE, attrs, "%s", filter);
+               ldb_ret = dsdb_search(samdb, local_ctx, &res,
+                                     dn, LDB_SCOPE_BASE, attrs,
+                                     DSDB_MARK_REQ_UNTRUSTED,
+                                     "%s", filter);
                if (ldb_ret != LDB_SUCCESS) {
                        result = map_ldb_error(local_ctx, ldb_ret,
                                               ldb_errstring(samdb), &errstr);
@@ -1497,6 +1509,7 @@ static NTSTATUS ldapsrv_CompareRequest(struct ldapsrv_call *call)
                }
        }
 
+reply:
        compare = &compare_r->msg->r.CompareResponse;
        compare->dn = NULL;
        compare->resultcode = result;
index 84b55f2d3622b7095e6b88824a0dd6e6afe63bfd..ccc29c062f2b35cf5077dd36c71d1229cf9e6a4d 100644 (file)
@@ -21,6 +21,7 @@ pull returned Success
         answers: ARRAY(0)
         nsrecs: ARRAY(1)
             nsrecs: struct dns_res_rec
+                start_ndr_offset         : 0x00000027 (39)
                 name                     : 'cnamedotprefix0.samba2003.example.com'
                 rr_type                  : DNS_QTYPE_CNAME (0x5)
                 rr_class                 : DNS_QCLASS_IN (0x1)